package cn.stylefeng.guns.modular.gridsystem.netty.tcp;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.stylefeng.guns.modular.gridsystem.mqtt.MyMqttClient;
import cn.stylefeng.guns.modular.gridsystem.netty.entity.RealTime;
import cn.stylefeng.guns.modular.gridsystem.netty.ws.MyChannelHandlerPool;
import cn.stylefeng.guns.modular.gridsystem.utils.ColorMapUtil;
import cn.stylefeng.guns.modular.gridsystem.utils.WavTransformUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import top.blesslp.librosa.core.Librosa;
import top.blesslp.librosa.core.Utils;
import top.blesslp.librosa.core.model.SpectrumEntity;
import top.blesslp.librosa.core.model.SpectrumEnums;

import javax.sound.sampled.AudioFormat;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static jdk.nashorn.internal.objects.Global.Infinity;

/**
 * @author ssy
 * @date 2022/4/11 14:10
 */
@Component
@ChannelHandler.Sharable
@Slf4j
public class TcpServerHandler extends SimpleChannelInboundHandler<Message> {

    private final Map<String, ByteArrayOutputStream> map = new ConcurrentHashMap<>();
    private final Map<String, ByteArrayOutputStream> specMap = new ConcurrentHashMap<>();
    private boolean silent = false;

    private RealTime realTime;

    @Value("${wav.min}")
    private Integer wavMin;

    @Autowired
    private MyMqttClient myMqttClient;

    //设置一个启动开关
    private boolean flag = false;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws IOException {

        //  System.out.println(">>>> channelRead0");
        // HashMap<String, Integer> map = new HashMap<>();
        //  int channel = msg.getDataGroup().getChannel();
        //  map.put("通道数量:", channel);
        if (flag && ObjectUtil.isNotNull(msg) && ObjectUtil.isNotNull(msg.getId())) {
            synchronized (this) {

                myMqttClient.publish("CONTROL/" + msg.getId(),  JSONUtil.toJsonStr(msg)); //JSONUtil.toJsonPrettyStr(msg)
            }
            // myMqttClient.publish("CONTROL/" + msg.getId(), "通道》》》" + JSONUtil.toJsonStr(map));
        }
    }


    // @Override
    protected void channelRead00(ChannelHandlerContext ctx, Message msg) throws IOException {
        //
        //   myMqttClient.publish(MqttTopicConstant.ALARM_PUSH, JSONUtil.toJsonStr(msg));
        RealTime realTime = new RealTime();
        realTime.setChannel("1");
        realTime.setChannelNum(2);
        realTime.setMapCategory("timeRange");
        realTime.setGatewayCode("011101110111");

        Message.DataGroup dataGroup = msg.getDataGroup();
        byte[] msg1 = dataGroup.getData();
        byte[] data = WavTransformUtil.leToBeBytes(msg1, msg1.length);
        List<byte[]> splitBytes = WavTransformUtil.splitBytes(data, 2048);

        if (ObjectUtil.isEmpty(realTime.getChannel()) || ObjectUtil.isEmpty(realTime.getMapCategory())) {
            return;
        }

        byte[] bytes = splitBytes.get(Integer.parseInt(realTime.getChannel()) - 1);

        int bit = dataGroup.getBits();
        int sampleRate = dataGroup.getSampleRate();
        int channelNum = dataGroup.getChannel();
        //定义缓冲区
        float cacheSecond = 1.024f; //存储cacheSecond秒数据

        int fftSize = sampleRate / 10;
        int hopLength = fftSize / 4;

        if (realTime.isSplCalibration()) {
            cacheSecond = 1.024f * 3;
        }
        ctx.channel().eventLoop().execute(() -> {
            AudioFormat wavFormat = new AudioFormat(sampleRate, bit, 1, true, false);
            Librosa.WaveData<float[]> waveData1 = Librosa.loadBytes(bytes, wavFormat);
            SpectrumEntity wavEntity = Librosa.getFeature(waveData1.data, sampleRate, bit, waveData1.data.length / 2, waveData1.data.length / 2, SpectrumEnums.SoundWav);

            float[] floatWav = (float[]) wavEntity.getyAxis();
            float calWav = WavTransformUtil.calculateAbsAverage(floatWav);
            silent = calWav < wavMin;
        });

        //16000 * 2 * 0.5 = 16000 bytes
        int cacheSize = (int) (sampleRate * 2 * cacheSecond);

        int cacheSpecSize = (int) (sampleRate * 2 * cacheSecond);

        ByteArrayOutputStream cacheOutputStream = map.get(realTime.getChannel());
        ByteArrayOutputStream specStream = specMap.get(realTime.getChannel());

        int status = 0;
        int cacheStatus = 0;

        //空通道的时候，创建缓冲区
        if (ObjectUtil.isNull(cacheOutputStream)) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(cacheSize);
            ByteArrayOutputStream specOutputStream = new ByteArrayOutputStream(cacheSpecSize);
            specOutputStream.write(bytes);
            byteArrayOutputStream.write(bytes);
            map.put(realTime.getChannel(), byteArrayOutputStream);
            specMap.put(realTime.getChannel(), specOutputStream);
            return;
        } else {
            //非空的时候，写入数据到缓冲区
            ByteArrayOutputStream byteArrayOutputStream = map.get(realTime.getChannel());
            ByteArrayOutputStream specOutputStream = specMap.get(realTime.getChannel());
            int size = byteArrayOutputStream.size();
            int specSize = specOutputStream.size();

            //原始数据
            if (size < cacheSize) {
                cacheStatus = 1;
                byteArrayOutputStream.write(bytes);
            }
            //语谱图
            if (specSize < cacheSpecSize) {
                status = 1;
                specOutputStream.write(bytes);
            }

        }


        realTime.setChannelNum(channelNum);

        if (status != 1) {


            byte[] specBytes = specStream.toByteArray();

            byte[] specBuffer = new byte[(int) (float) hopLength];
            System.arraycopy(specBytes, specBytes.length - specBuffer.length, specBuffer, 0, specBuffer.length);

            specStream.reset();
            specStream.write(specBuffer, 0, specBuffer.length);

            ctx.channel().eventLoop().execute(() -> {
                AudioFormat audioFormat = new AudioFormat(sampleRate, bit, 1, true, false);
                Librosa.WaveData<float[]> specData = Librosa.loadBytes(specBytes, audioFormat);
                if ("timeRange".equals(realTime.getMapCategory())) {
                    SpectrumEntity feature = Librosa.getFeature(specData.data, sampleRate, bit, fftSize, hopLength, SpectrumEnums.SpectrogramRT);
                    HashMap<String, Object> hashMap = new HashMap<>();
                    if (ObjectUtil.isNotNull(feature)) {
                        float[] xAxis = feature.getXAxis();
                        float[] yAxis = (float[]) feature.getyAxis();

                        HashMap<String, Object> map = new HashMap<>();
                        map.put("xData", xAxis);
                        map.put("yData", yAxis);
                        float[][] colorData = (float[][]) feature.getData();
                        float[] floats1 = colorData[0];
                        List<Float> colors = new ArrayList<>();
                        float min = 0;
                        double max;
                        max = Utils.max(colorData);
                        for (int i = 0; i < floats1.length; i++) {
                            for (float[] colorDatum : colorData) {
                                colors.add(colorDatum[i]);
                            }
                        }
                        ArrayList<Integer> colorDatas = new ArrayList<>();
                        double[][] colorMap = ColorMapUtil.colorMap;
                        for (Float color : colors) {
                            int a = silent && realTime.isNoise() ? 0 : Math.min(255, (int) Math.floor((((color - min) * 255.0) / (max - min))));
                            double[] doubles = colorMap[a];
                            int R = (int) (doubles[0] * 255);
                            int G = (int) (doubles[1] * 255);
                            int B = (int) (doubles[2] * 255);
                            int A = (int) (doubles[3] * 255);
                            colorDatas.add(R);
                            colorDatas.add(G);
                            colorDatas.add(B);
                            colorDatas.add(A);
                        }

                        map.put("originalData", colors);
                        map.put("colorData", colorDatas);
                        hashMap.put("chartData", map);
                    }

                    hashMap.put("mapCategory", realTime.getMapCategory());
                    hashMap.put("channel", realTime.getChannel());
                    hashMap.put("gatewayCode", realTime.getGatewayCode());

                    String str = JSONUtil.toJsonStr(hashMap);
                    MyChannelHandlerPool.sendAllMessage(str);
                }
            });
        }

        if (cacheStatus != 1) {
            byte[] cacheBuffer = cacheOutputStream.toByteArray();
            cacheOutputStream.reset();

            ctx.channel().eventLoop().execute(() -> {
                AudioFormat audioFormat = new AudioFormat(sampleRate, bit, 1, true, false);
                Librosa.WaveData<float[]> waveData = Librosa.loadBytes(cacheBuffer, audioFormat);

                SpectrumEntity soundPressLevelFeature1 = Librosa.getFeature(waveData.data, sampleRate, bit, fftSize, hopLength, SpectrumEnums.SoundPressLevel);

                HashMap<String, Object> hashMap = new HashMap<>();

                float[] floats = (float[]) soundPressLevelFeature1.getyAxis();

                if (floats.length > 0) {
                    double v = WavTransformUtil.calculateAverage(floats);
                    // log.info("声压级：{}", v);
                    if (realTime.isSplCalibration()) {
                        if (v != -Infinity) {
                            realTime.setSoundPressureLevelOffset(realTime.getStandardSpl() - v);
                            realTime.setSplCalibration(false);
                            hashMap.put("splLoad", false);
                            String str = JSONUtil.toJsonStr(hashMap);
                            MyChannelHandlerPool.sendAllMessage(str);
                        }
                    }
                    if (v != -Infinity) {
                        hashMap.put("sourceSpl", v);
                        realTime.setSoundPressureLevel(v + realTime.getSoundPressureLevelOffset());
                    }
                }

                SpectrumEntity feature = null;
                if ("time".equals(realTime.getMapCategory())) {
                    feature = Librosa.getFeature(waveData.data, sampleRate, bit, fftSize, hopLength, SpectrumEnums.SoundWav);
                } else if ("range".equals(realTime.getMapCategory())) {
                    feature = Librosa.getFeature(waveData.data, sampleRate, bit, fftSize, hopLength, SpectrumEnums.FFT);
                } else {
                    hashMap.put("data", waveData.data);
                }

                if (ObjectUtil.isNotNull(feature)) {
                    float[] xAxis = feature.getXAxis();
                    float[] yAxis = (float[]) feature.getyAxis();

                    HashMap<String, Object> map = new HashMap<>();
                    if ("time".equals(realTime.getMapCategory())) {
                        String[] xData = new String[xAxis.length];
                        for (int i = 0; i < xAxis.length; i++) {
                            xData[i] = String.format("%.2f", xAxis[i]);
                        }
                        map.put("xData", xData);
                    } else {
                        map.put("xData", xAxis);
                    }
                    map.put("yData", yAxis);
                    hashMap.put("chartData", map);
                    hashMap.put("data", waveData.data);

                }

                hashMap.put("soundPressureLevel", String.format("%.1f", realTime.getSoundPressureLevel()));
                hashMap.put("mapCategory", realTime.getMapCategory());
                hashMap.put("channel", realTime.getChannel());
                hashMap.put("gatewayCode", realTime.getGatewayCode());

                String str = JSONUtil.toJsonStr(hashMap);
                MyChannelHandlerPool.sendAllMessage(str);
            });
        }

    }

    //数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将数据写入到缓存并刷新
        //一般来讲要对发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("数据接收到了", CharsetUtil.UTF_8));
    }

    //处理异常，一般把通道关闭
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        System.out.println("客户端与服务端连接异常..." + cause.toString());
        ctx.channel().close();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端与服务端连接关闭...");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端与服务端连接开始..." + ctx.channel().remoteAddress());
    }

}
